]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
screencastService: Allow for file extensions depending on encoder used
authorJonas Dreßler <verdre@v0yd.nl>
Sun, 22 Jan 2023 18:43:53 +0000 (19:43 +0100)
committerFlorian Müllner <fmuellner@gnome.org>
Fri, 1 Mar 2024 12:08:54 +0000 (12:08 +0000)
Depending on the encoder we want to use a different container format and
therefore a different file extension. Right now this file extension is
forced to be webm, so shuffle things around a bit to make that more
dynamic.

Note that this also introduces removing for the old file created by the
recorder, otherwise it would create an empty "mp4" file every time it
falls back from "mp4" to "webm".

To be nice to external (ie. not gnome-shell) consumers of the
screencastService, let's not break the API completely, and detect the
".webm" suffix a little longer to not end up with weird file.webm.webm
filenames.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3211>

js/dbusServices/screencast/screencastService.js
js/ui/screenshot.js

index 76222d7e2bd56afa76dfb8dafd8a248da442f02e..ac0e461c653cc2ae65c94974232afec413da2e03 100644 (file)
@@ -33,6 +33,7 @@ const PIPELINE_BLOCKLIST_FILENAME = 'gnome-shell-screencast-pipeline-blocklist';
 const PIPELINES = [
     {
         id: 'swenc-dmabuf-vp8-vp8enc',
+        fileExtension: 'webm',
         pipelineString:
             'capsfilter caps=video/x-raw(memory:DMABuf),max-framerate=%F/1 ! \
              glupload ! glcolorconvert ! gldownload ! \
@@ -43,6 +44,7 @@ const PIPELINES = [
     },
     {
         id: 'swenc-memfd-vp8-vp8enc',
+        fileExtension: 'webm',
         pipelineString:
             'capsfilter caps=video/x-raw,max-framerate=%F/1 ! \
              videoconvert chroma-mode=none dither=none matrix-mode=output-only n-threads=%T ! \
@@ -69,7 +71,7 @@ const SessionState = {
 };
 
 class Recorder extends Signals.EventEmitter {
-    constructor(sessionPath, x, y, width, height, filePath, options,
+    constructor(sessionPath, x, y, width, height, filePathStem, options,
         invocation) {
         super();
 
@@ -79,10 +81,10 @@ class Recorder extends Signals.EventEmitter {
         this._y = y;
         this._width = width;
         this._height = height;
-        this._filePath = filePath;
+        this._filePathStem = filePathStem;
 
         try {
-            const dir = Gio.File.new_for_path(filePath).get_parent();
+            const dir = Gio.File.new_for_path(filePathStem).get_parent();
             dir.make_directory_with_parents(null);
         } catch (e) {
             if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
@@ -232,6 +234,11 @@ class Recorder extends Signals.EventEmitter {
     }
 
     _tryNextPipeline() {
+        if (this._filePath) {
+            GLib.unlink(this._filePath);
+            delete this._filePath;
+        }
+
         const {done, value: pipelineConfig} = this._pipelineConfigs.next();
         if (done) {
             this._handleFatalPipelineError('All pipelines failed to start',
@@ -348,7 +355,7 @@ class Recorder extends Signals.EventEmitter {
                 newState === Gst.State.PLAYING) {
                 this._pipelineState = PipelineState.PLAYING;
 
-                this._startRequest.resolve();
+                this._startRequest.resolve(this._filePath);
                 delete this._startRequest;
             }
 
@@ -442,8 +449,9 @@ class Recorder extends Signals.EventEmitter {
     }
 
     _createPipeline(nodeId, pipelineConfig, framerate) {
-        const {pipelineString} = pipelineConfig;
+        const {fileExtension, pipelineString} = pipelineConfig;
         const finalPipelineString = this._substituteVariables(pipelineString, framerate);
+        this._filePath = `${this._filePathStem}.${fileExtension}`;
 
         const fullPipeline = `
             pipewiresrc path=${nodeId}
@@ -544,6 +552,13 @@ export const ScreencastService = class extends ServiceImplementation {
         let filename = '';
         let escape = false;
 
+        // FIXME: temporarily detect and strip .webm prefix to avoid breaking
+        // external consumers of our API, remove this again
+        if (template.endsWith('.webm')) {
+            console.log("'file_template' for screencast includes '.webm' file-extension. Passing the file-extension as part of the filename has been deprecated, pass the 'file_template' without a file-extension instead.");
+            template = template.substring(0, template.length - '.webm'.length);
+        }
+
         [...template].forEach(c => {
             if (escape) {
                 switch (c) {
@@ -605,7 +620,7 @@ export const ScreencastService = class extends ServiceImplementation {
 
         const [fileTemplate, options] = params;
         const [screenWidth, screenHeight] = this._introspectProxy.ScreenSize;
-        const filePath = this._generateFilePath(fileTemplate);
+        const filePathStem = this._generateFilePath(fileTemplate);
 
         let recorder;
 
@@ -614,7 +629,7 @@ export const ScreencastService = class extends ServiceImplementation {
                 sessionPath,
                 0, 0,
                 screenWidth, screenHeight,
-                filePath,
+                filePathStem,
                 options,
                 invocation);
         } catch (error) {
@@ -629,8 +644,8 @@ export const ScreencastService = class extends ServiceImplementation {
         this._addRecorder(sender, recorder);
 
         try {
-            await recorder.startRecording();
-            invocation.return_value(GLib.Variant.new('(bs)', [true, filePath]));
+            const pathWithExtension = await recorder.startRecording();
+            invocation.return_value(GLib.Variant.new('(bs)', [true, pathWithExtension]));
         } catch (error) {
             log(`Failed to start recorder: ${error.message}`);
             this._removeRecorder(sender);
@@ -676,7 +691,7 @@ export const ScreencastService = class extends ServiceImplementation {
         const [sessionPath] = this._proxy.CreateSessionSync({});
 
         const [x, y, width, height, fileTemplate, options] = params;
-        const filePath = this._generateFilePath(fileTemplate);
+        const filePathStem = this._generateFilePath(fileTemplate);
 
         let recorder;
 
@@ -685,7 +700,7 @@ export const ScreencastService = class extends ServiceImplementation {
                 sessionPath,
                 x, y,
                 width, height,
-                filePath,
+                filePathStem,
                 options,
                 invocation);
         } catch (error) {
@@ -700,8 +715,8 @@ export const ScreencastService = class extends ServiceImplementation {
         this._addRecorder(sender, recorder);
 
         try {
-            await recorder.startRecording();
-            invocation.return_value(GLib.Variant.new('(bs)', [true, filePath]));
+            const pathWithExtension = await recorder.startRecording();
+            invocation.return_value(GLib.Variant.new('(bs)', [true, pathWithExtension]));
         } catch (error) {
             log(`Failed to start recorder: ${error.message}`);
             this._removeRecorder(sender);
index 77d4c0c6f798278b8a24917e87c89404307258e5..f78eeacd13a9c4cc0d9a5909503661aef6eb6caf 100644 (file)
@@ -1998,9 +1998,9 @@ export const ScreenshotUI = GObject.registerClass({
                     _('Screencasts'),
                     /* Translators: this is a filename used for screencast
                      * recording, where "%d" and "%t" date and time, e.g.
-                     * "Screencast from 07-17-2013 10:00:46 PM.webm" */
+                     * "Screencast from 07-17-2013 10:00:46 PM" */
                     /* xgettext:no-c-format */
-                    _('Screencast from %d %t.webm'),
+                    _('Screencast from %d %t'),
                 ]),
                 {'draw-cursor': new GLib.Variant('b', drawCursor)});